import { computed, onScopeDispose, ref, unref } from 'vue';
import { random, ticker } from '../utils';
const defaultOptions = {
    minSpeed: 0.1,
    maxSpeed: 0.4,
    accelTime: 2500,
    decelTime: 2500,
    easeIn: (k) => k * k,
    easeOut: (k) => k * (2 - k),
    startDelay: 0,
    endDelay: 0,
};
export const useLuckyDraw = (options) => {
    const { count, minSpeed, maxSpeed, accelTime, decelTime, complete, easeIn, easeOut, startDelay, endDelay, } = Object.assign({}, defaultOptions, options);
    let stage = 'INITIAL';
    let currentIndex = 0;
    const activeIndex = ref();
    const playing = ref(false);
    let startTime = 0;
    let accelFrames = 0;
    let decelStopIndex;
    let decelStopActiveIndex;
    let decelStartIndex = 0;
    const getDecelStopIndex = () => {
        const interval = unref(accelTime) / accelFrames;
        let i = 0;
        let mockCurrentIndex = currentIndex;
        let mockActiveIndex = 0;
        while (++i) {
            let progress = (interval * i) / unref(decelTime);
            if (progress >= 1) {
                progress = 1;
            }
            const currentSpeed = unref(maxSpeed) -
                easeOut(progress) * (unref(maxSpeed) - unref(minSpeed));
            mockCurrentIndex += currentSpeed;
            mockActiveIndex = mockCurrentIndex % unref(count) >> 0;
            if (progress === 1 && mockActiveIndex === decelStopActiveIndex) {
                break;
            }
        }
        return mockCurrentIndex >> 0;
    };
    const tick = () => {
        switch (stage) {
            case 'START_DELAY': {
                if (Date.now() - startTime >= unref(startDelay)) {
                    stage = 'ACCELERATED';
                    startTime = Date.now();
                }
                break;
            }
            case 'ACCELERATED': {
                accelFrames++;
                let progress = (Date.now() - startTime) / unref(accelTime);
                if (progress >= 1) {
                    progress = 1;
                }
                const currentSpeed = unref(minSpeed) +
                    easeIn(progress) * (unref(maxSpeed) - unref(minSpeed));
                currentIndex += currentSpeed;
                if (progress === 1) {
                    stage = 'CONSTANT_SPEED';
                }
                break;
            }
            case 'CONSTANT_SPEED': {
                currentIndex += unref(maxSpeed);
                if (decelStopActiveIndex !== undefined) {
                    stage = 'DECELERATED';
                    startTime = Date.now();
                }
                break;
            }
            case 'DECELERATED': {
                if (decelStopIndex === undefined) {
                    decelStopIndex = getDecelStopIndex();
                    decelStartIndex = currentIndex;
                }
                let progress = (Date.now() - startTime) / unref(decelTime);
                if (progress >= 1) {
                    progress = 1;
                }
                currentIndex =
                    decelStartIndex +
                        easeOut(progress) * (decelStopIndex - decelStartIndex);
                if (progress === 1) {
                    if (unref(endDelay) === 0) {
                        stage = 'DONE';
                    }
                    else {
                        stage = 'END_DELAY';
                        startTime = Date.now();
                    }
                }
                break;
            }
            case 'END_DELAY': {
                if (Date.now() - startTime >= unref(endDelay)) {
                    stage = 'DONE';
                }
                break;
            }
            case 'DONE': {
                const index = decelStopActiveIndex;
                pause();
                complete?.(index);
                break;
            }
        }
        activeIndex.value = currentIndex;
    };
    const play = () => {
        if (playing.value) {
            return;
        }
        playing.value = true;
        startTime = Date.now();
        if (unref(startDelay) === 0) {
            stage = 'ACCELERATED';
        }
        else {
            stage = 'START_DELAY';
        }
        ticker.add(tick);
    };
    const pause = () => {
        ticker.remove(tick);
        playing.value = false;
        startTime = 0;
        accelFrames = 0;
        decelStopIndex = undefined;
        decelStopActiveIndex = undefined;
        decelStartIndex = 0;
    };
    const reset = () => {
        pause();
        stage = 'INITIAL';
        currentIndex = 0;
        activeIndex.value = undefined;
    };
    const stop = (index) => {
        if (decelStopActiveIndex === undefined) {
            decelStopActiveIndex =
                index === undefined ? random(0, unref(count) - 1) : index;
        }
    };
    onScopeDispose(() => {
        pause();
    });
    return {
        play,
        stop,
        pause,
        reset,
        playing: computed(() => playing.value),
        activeIndex: computed(() => activeIndex.value),
    };
};
